<?php

declare(strict_types=1);

namespace App\Utils;

use Closure;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\Pay\Application;
use EasyWeChat\Pay\Message;
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpServer\Contract\RequestInterface;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Throwable;

use function Hyperf\Config\config;

class WechatPay
{
    private Application $app;

    public function __construct()
    {
        $this->app = new Application(config('wechat.pay'));
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        $this->app->setRequest($request);
    }

    /**
     * JSAPI下单
     * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
     * @param string $openId
     * @param string $outTradeNo
     * @param string $amount
     * @return string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public function prepay(string $openId, string $outTradeNo, string $amount): string
    {
        $response = $this->app->getClient()->postJson('v3/pay/transactions/jsapi', [
            'appid' => config('wechat.mini_app.app_id'),
            'mchid' => config('wechat.pay.mch_id'),
            'description' => '租车订单支付',
            'out_trade_no' => $outTradeNo,
            'notify_url' => config('wechat.pay.notify_url'),
            'amount' => [
                'total' => config('app_env') === 'prod' ? $amount * 100 : 1,
                'currency' => 'CNY',
            ],
            'payer' => [
                'openid' => $openId,
            ],
        ]);
        return $response['prepay_id'];
    }

    /**
     * 构建小程序支付参数
     * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
     * @param string $prepayId
     * @return mixed[]
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public function buildMiniAppConfig(string $prepayId): array
    {
        return $this->app->getUtils()->buildMiniAppConfig($prepayId, config('wechat.mini_app.app_id'));
    }

    /**
     * 通知
     * @param Closure $closure
     * @return ResponseInterface
     */
    public function notify(Closure $closure): ResponseInterface
    {
        try {
            $server = $this->app->getServer();
            $server->handlePaid(function (Message $message, Closure $next) use ($closure) {
                $this->app->getValidator()->validate($this->app->getRequest());
                $closure($message);
                return $next($message);
            });
            return $server->serve();
        } catch (Throwable $e) {
            return new Response(
                500,
                [],
                strval(json_encode(['code' => 'ERROR', 'message' => $e->getMessage()], JSON_UNESCAPED_UNICODE))
            );
        }
    }

    /**
     * 商户订单号查询
     * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml
     * @param string $outTradeNo
     * @return mixed
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public function queryOrder(string $outTradeNo): mixed
    {
        return $this->app->getClient()->get(sprintf('v3/pay/transactions/out-trade-no/%s', $outTradeNo), [
            'query' => [
                'mchid' => config('wechat.pay.mch_id'),
            ],
        ]);
    }
}
